/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package health;

//import health.entities.Paciente;
//import health.persistence.PacienteJpaController;
import ccrClasses.layer1.results.Result;
import ccrClasses.layer1.results.Results;
import ccrClasses.layer2.Code;
import ccrClasses.layer2.CodingSystem;
import ccrClasses.layer2.DateTime;
import ccrClasses.layer2.Description;
import ccrClasses.layer2.ExactDateTime;
import ccrClasses.layer2.Test;
import ccrClasses.layer2.TestResult;
import ccrClasses.layer2.Text;
import ccrClasses.layer2.Type;
import ccrClasses.layer2.Value;
import com.google.gdata.client.GoogleService;
import com.google.gdata.data.Entry;
import com.google.gdata.data.PlainTextConstruct;
import com.google.gdata.util.AuthenticationException;
import com.google.gdata.util.XmlBlob;
import health.entities.Paciente;
import health.persistence.PacienteDAO;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.servlet.RequestDispatcher;
import javax.servlet.Servlet;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.FileItemFactory;
import org.apache.commons.fileupload.FileUploadException;
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import org.apache.commons.fileupload.servlet.ServletFileUpload;
import parsers.SMXMLParser;
import smXmlMapping.Exame;
import smXmlMapping.PacienteXML;
import smXmlMapping.Resultado;

/**
 *
 * @author matheus
 */
public class HealthSampleServlet extends HttpServlet implements Servlet {

    public static final long serialVersionUID = 1;
    // the name of the session variable that holds our AuthSub token
    public static final String HEALTH_TOKEN = "healthToken";

    private static boolean flag = true;

    private final String FEED_BASE_PATH = "http://www.google.com/h9/feeds/";

    private final String PROFILE_FEED_PATH = FEED_BASE_PATH + "profile/";

    private final String REGISTER_FEED_PATH_REGISTER = FEED_BASE_PATH + "register/";

    private final String REGISTER_FEED_PATH = REGISTER_FEED_PATH_REGISTER + "default";

    private ArrayList<Exame> examesEmCurso;

    private PacienteXML pacienteEmCurso;

    private SMXMLParser smxmlp;

    private PacienteDAO pacienteDAO;

    private String masterURL;

    private final String SEM_MAIS_TESTES = "Não há mais testes a serem adicionados.";

    private final String FORMAT_ERROR = "Formato do arquivo .xml está incorreto.";

    private final String EXTENSAO_INCORRETA = "Arquivo com extensão incorreta.<br>" +
            "A extensão do arquivo deve ser '.xml'";

    private final String ERRO_COMUNICAÇÃO = "Ocorreu um erro ao comunicar-se com seu perfil.<br>" +
            "Verifique sua conexão com a internet.";
    
    private final String NO_AUTH = "O usuário não deu autorização.";

    private final String OK = "Exames enviados com sucesso.";



    /**
     * Process GET requests by calling the doPost method
     *
     * @param request The request
     * @param response The response
     * @throws ServletException
     * @throws IOException
     */
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        processPostRequests(request, response);
    }

    /**
     * Process all requests to this servlet.
     *
     * @param request The request
     * @param response The response
     * @throws ServletException
     * @throws IOException
     */
    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        processPostRequests(request, response);

    }

    /**
     * Process all get requests to this servlet.
     * 
     * @param request
     * @param response
     * @throws ServletException
     * @throws IOException
     */
    private void processGetRequests(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {

        String param = request.getParameter("codigo");
        PacienteDAO pacienteDAO = new PacienteDAO();
        Paciente paciente = pacienteDAO.resgatarPaciente(Integer.parseInt(param));
        RequestDispatcher dispatcher = null;
        if (paciente == null) {
            request.setAttribute("error", "O paciente " + param + "não está registrado");
            dispatcher = getServletContext().getRequestDispatcher(
                    "/initialPage.html");
            dispatcher.forward(request, response);
        } else {
            if (paciente.getToken() == null) {
                String authUrl = HealthSample.getAuthSubUrl(getCurrentUrl(request) + "?action=handleToken&id=" + param);
                response.sendRedirect(authUrl);
            } else {
                request.setAttribute("paciente_id", paciente.getId());
                request.setAttribute("paciente_nome", paciente.getNome());
                request.setAttribute("paciente_token", paciente.getToken());
                dispatcher = getServletContext().getRequestDispatcher(
                        "/outputPatient.jsp");
                dispatcher.forward(request, response);
            }
        }
    }

    /**
     * Process all post requests to this servlet.
     *
     * @param request
     * @param response
     * @throws ServletException
     * @throws IOException
     */
    private void processPostRequests(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        /*
         * Determine action attempted
         */
        String action = (request.getParameter("action")) == null
                ? "processaArquivo" : request.getParameter("action");

//        String action = (request.getParameter("action")) == null
//                ? "initialPage" : request.getParameter("action");
//
//        String action = (request.getParameter("action")) == null
//                ? "outputIntroPage" : request.getParameter("action");

        if ("initialPage".equals(action)) {
            processGetRequests(request, response);
        } else if ("handleToken".equals(action)) {
            processAcceptAuthSubToken(request, response);
        } else if ("outputProfile".equals(action)) {
            processOutputProfile(request, response);
        } else if ("logout".equals(action)) {
            processLogout(request, response);
        } else if ("processaArquivo".equals(action)) {
            processaArquivoDeEntrada(request, response);
        } else if ("handleConnException".equals(action)) {
            processaExcecaoComunicacao(request, response);
        } else {
            throw new ServletException("Error! Bad Action!'" + action + "'");
        }


//        if ("outputIntroPage".equals(action)) {
//            processOutputIntroPage(request, response);
//        } else if ("handleToken".equals(action)) {
//            processAcceptAuthSubToken(request, response);
//        } else if ("outputProfile".equals(action)) {
//            processOutputProfile(request, response);
//        } else if ("submitNotice".equals(action)) {
//            processSubmittedNotice(request, response);
//        } else if ("logout".equals(action)) {
//            processLogout(request, response);
//        } else {
//            throw new ServletException("Error! Bad Action!'" + action + "'");
//        }
    }

    /**
     * Returns URI of the current page
     *
     * @param request the request
     * @return the URI the user is currently requesting.
     * @throws MalformedURLException When the Java URL constructor gets angry.
     */
    private String getCurrentUrl(HttpServletRequest request)
            throws MalformedURLException {
        URL currentUrl = new URL(request.getScheme(), request.getServerName(),
                request.getServerPort(), request.getRequestURI());
        return currentUrl.toString();
    }

    /**
     * Save AuthSubToken into session.
     *
     * @param request The request
     * @param response The response
     * @throws IOException
     */
    private void processAcceptAuthSubToken(HttpServletRequest request,
            HttpServletResponse response) throws IOException {
        /*
         * Request is caused by a user being redirected back from AuthSub login
         */
        if (request.getParameter("token") != null) {
            HealthSample sp = new HealthSample();           
            int id = Integer.parseInt(pacienteEmCurso.getPaciente());
            Paciente paciente = new Paciente(id,pacienteEmCurso.getNomePaciente());            
            
            try {
                String token = sp.exchangeAuthSubToken(request.getParameter("token"),
                        request.getParameter("id"));

                paciente.setToken(token);

                pacienteDAO.criarOuAtualizarPaciente(paciente);

                enviarExames(examesEmCurso, pacienteEmCurso, token);

                //pacienteDAO.fechaSessao();

                request.getSession().setAttribute("title", "ÊXITO");
                request.getSession().setAttribute("msg", OK);
                request.getSession().setAttribute("returnURL", masterURL);
                request.getRequestDispatcher("/msg.jsp").forward(request, response);

                //request.getRequestDispatcher("/upload.jsp").forward(request, response);

                //request.getSession().setAttribute(HEALTH_TOKEN, token);
                //processOutputProfile(request, response);
                //response.sendRedirect("?action=outputProfile");
                //} catch (ServletException ex) {
                //    Logger.getLogger(HealthSampleServlet.class.getName()).log(Level.SEVERE, null, ex);
            } catch (ServletException ex) {
                Logger.getLogger(HealthSampleServlet.class.getName()).log(Level.SEVERE, null, ex);
            } catch (HealthSampleException e) {
                System.err.println("Authentication exception: " + e.getMessage());
            }
        }else{
            try {
                request.getSession().setAttribute("msg", NO_AUTH);
                request.getRequestDispatcher("/error.jsp").forward(request, response);
            } catch (ServletException ex) {
                Logger.getLogger(HealthSampleServlet.class.getName()).log(Level.SEVERE, null, ex);
            }
        }
    }

    /**
     * Logs the user out by clearing the session.
     *
     * @param request The request
     * @param response The response
     * @throws IOException
     */
    private void processLogout(HttpServletRequest request,
            HttpServletResponse response) throws IOException {
        request.getSession().removeAttribute(HEALTH_TOKEN);
        response.sendRedirect("?action=initialPage");
//        response.sendRedirect("?action=outputIntroPage");
    }

    /**
     * Default action - output intro page.
     *
     * @param request The request
     * @param response The response
     * @throws ServletException
     * @throws IOException
     */
    private void processOutputIntroPage(HttpServletRequest request,
            HttpServletResponse response) throws ServletException, IOException {

        String authUrl = HealthSample.getAuthSubUrl(getCurrentUrl(request) + "?action=handleToken");
        request.setAttribute("authUrl", authUrl);
        RequestDispatcher dispatcher = getServletContext().getRequestDispatcher(
                "/outputSecondPage.jsp");
        dispatcher.forward(request, response);
    }

    /**
     * Output profile visible from the given token.
     *
     * @param request The request
     * @param response The response
     * @throws ServletException
     * @throws IOException
     */
    private void processOutputProfile(HttpServletRequest request,
            HttpServletResponse response) throws ServletException, IOException {
        HealthSample sp = new HealthSample();
        try {
            String token = (String) request.getSession().getAttribute(HEALTH_TOKEN);
            if (token == null) {
                response.sendRedirect("?action=initialPage");
                //response.sendRedirect("?action=outputIntroPage");
                return;
            }
            sp.setAuthSubToken(token);

            request.setAttribute("profile", sp.getProfile());
        } catch (HealthSampleException e) {
            System.err.println("Profile retrieval exception: " + e.getMessage());
        }
        RequestDispatcher dispatcher = getServletContext().getRequestDispatcher(
                "/outputProfile.jsp");
        dispatcher.forward(request, response);
    }

    /**
     * Action: User submitted a new notice to post to the profile.
     *
     * @param request
     * @param response
     * @throws IOException
     */
    private void processSubmittedNotice(HttpServletRequest request,
            HttpServletResponse response) throws IOException {
        HealthSample sp = new HealthSample();
        try {
            String token = (String) request.getSession().getAttribute(HEALTH_TOKEN);
            if (token == null) {
                response.sendRedirect("?action=outputIntroPage");
                return;
            }
            sp.setAuthSubToken(token);


            String subject = request.getParameter("subject");
            String message = request.getParameter("message");
            String ccrg = request.getParameter("ccrg");

            sp.postNotice(subject, message, ccrg);

            response.sendRedirect("?action=outputProfile");

        } catch (HealthSampleException e) {
            System.err.println("Exception posting notice: " + e.getMessage());
        }
    }

    private void processaArquivoDeEntrada(HttpServletRequest request,
            HttpServletResponse response) throws IOException {
        try {

            configuraProxy(request,response);

            masterURL = request.getScheme()+"://"+ request.getServerName() + ":" + request.getContextPath();

            // Create a factory for disk-based file items
            FileItemFactory factory = new DiskFileItemFactory();

            // Create a new file upload handler
            ServletFileUpload upload = new ServletFileUpload(factory);
            List<FileItem> items = upload.parseRequest(request);

            smxmlp = new SMXMLParser();
            String xmlString = items.get(0).getString();
            xmlString = xmlString.trim();
            ByteArrayInputStream stream = new ByteArrayInputStream(xmlString.getBytes());
            smxmlp.mapXmlFile(stream);

//          ArrayList<Exame> exames = smxmlp.extractExames();
//          PacienteXML pacienteXML = smxmlp.parseUser();

            examesEmCurso = smxmlp.extractExames();

            pacienteEmCurso = smxmlp.parseUser();

            String idString = pacienteEmCurso.getPaciente();
            int id = Integer.parseInt(idString);

            pacienteDAO = new PacienteDAO();

            Paciente p = checarPaciente(id, pacienteDAO);

            if(p != null) {

                //Paciente p = pacienteDAO.resgatarPaciente(id);

                HealthSample healthSample = new HealthSample();

                Object obj = healthSample.getProfileResults(p);
                
                if (obj instanceof List || obj == null) {
                    ArrayList<Exame> examesFiltrados = filtrarExames(examesEmCurso, (ArrayList<ArrayList>) obj);
                    if (examesFiltrados != null && examesFiltrados.size() > 0) {
                        //envia exames
                        enviarExames(examesFiltrados, pacienteEmCurso, p.getToken());
                        request.getSession().setAttribute("title", "ÊXITO");
                        request.getSession().setAttribute("msg", OK);
                        request.getSession().setAttribute("returnURL", masterURL);
                        request.getRequestDispatcher("/msg.jsp").forward(request, response);
                    } else {
                        //mostra menssagem para o usuário avisando q o(s) exame(s) ja está lá
                        request.getSession().setAttribute("msg", SEM_MAIS_TESTES);
                        request.getSession().setAttribute("returnURL", masterURL);
                        request.getRequestDispatcher("/error.jsp").forward(request, response);
                    }

                } else if (obj instanceof AuthenticationException) {
                    String authUrl = HealthSample.getAuthSubUrl(getCurrentUrl(request) +
                            "?action=handleConnException&id=" + Integer.parseInt(idString));
                    response.sendRedirect(authUrl);
                } else if (obj instanceof UnknownHostException) {
                    request.getSession().setAttribute("msg", ERRO_COMUNICAÇÃO);
                    request.getRequestDispatcher("/error.jsp").forward(request, response);
                }

            } else {
                //encaminha o usuário para autenticação e autorização
                //pega o token e um abraço
                //...
                String nextUrl = getCurrentUrl(request);
                System.out.println(nextUrl);
                String authUrl = HealthSample.getAuthSubUrl( nextUrl +
                        "?action=handleToken&id=" + Integer.parseInt(idString));
                response.sendRedirect(authUrl);
            }

        } catch (ServletException ex) {
            Logger.getLogger(HealthSampleServlet.class.getName()).log(Level.SEVERE, null, ex);
        } catch (HealthSampleException ex) {
            Logger.getLogger(HealthSampleServlet.class.getName()).log(Level.SEVERE, null, ex);
        } catch (FileUploadException ex) {
            Logger.getLogger(HealthSampleServlet.class.getName()).log(Level.SEVERE, null, ex);
        } catch (NullPointerException ex) {
            //Logger.getLogger(HealthSampleServlet.class.getName()).log(Level.SEVERE, null, ex);
            request.getSession().setAttribute("msg", FORMAT_ERROR);
            try {
                request.getRequestDispatcher("/error.jsp").forward(request, response);
            } catch (ServletException ex1) {
                Logger.getLogger(HealthSampleServlet.class.getName()).log(Level.SEVERE, null, ex1);
            }
        }
    }

    private Paciente checarPaciente(int idPaciente,PacienteDAO pacienteDAO) {
        Paciente p = pacienteDAO.resgatarPaciente(idPaciente);
        if (p == null) {
            return null;
        }

        return p;
    }

    private ArrayList<Exame> filtrarExames(List examesSH, ArrayList<ArrayList> examesGH) {
        ArrayList<Exame> novosExames = new ArrayList<Exame>();
        ArrayList<Exame> examesJaExistentes = new ArrayList<Exame>();

        for (int i = 0; i < examesSH.size(); i++) {
            Exame e = (Exame) examesSH.get(i);
            if (examesGH.get(0) == null) {
                novosExames.add(e);
            } else {
                if (!examesGH.get(0).contains(e.getDataResultado() + "#" + e.getCodigo())) {
                    novosExames.add(e);
                } else {
                    for (Resultado r : e.getResultados()) {
                        if (!examesGH.get(1).contains(e.getDataResultado() + "#" + e.getCodigo() +
                                "@" + r.getAmostra() + "$" + r.getAtributo())) {
                            Exame nil = new Exame();
                            nil.setCodigo(e.getCodigo());
                            nil.setDataResultado(e.getDataResultado());
                            nil.setMetodo(e.getMetodo());
                            nil.setNome(e.getNome());
                            nil.setObs(e.getObs());
                            ArrayList<Resultado> rs = new ArrayList<Resultado>();
                            rs.add(r);
                            nil.setResultado(rs);
                            nil.setUsuarioLib(e.getUsuarioLib());
                            novosExames.add(nil);
                        }
                    }
                }


            }
        }
        return novosExames;
    }

    private void enviarExames(ArrayList<Exame> examesParaEnviar, PacienteXML pacienteXML, String authSubToken) {

        GoogleService h9Service = new GoogleService("weaver", "TCC_PEP");

        h9Service.setAuthSubToken(authSubToken);

        Entry newNotice = new Entry();

        String dataHora[] = splitXmlData(examesParaEnviar.get(0).getDataResultado());

        String tituloNoticia = "Resultados de exames emitidos em: " +
                " " + dataHora[0] + " às " + dataHora[1];
        String assuntoNoticia = "Paciente: " + pacienteXML.getNomePaciente();
        newNotice.setTitle(new PlainTextConstruct(tituloNoticia));
        newNotice.setContent(new PlainTextConstruct(assuntoNoticia));

        Results results = new Results();

        for (int i = 0; i < examesParaEnviar.size(); i++) {
            Result result = new Result();
            result.setDateTime(new DateTime(new Type(new Text(Type.COLLECTION_START_DATE)),
                    new ExactDateTime(formatData(examesParaEnviar.get(i).getDataResultado()))));
            Code code = new Code();
            code.setValue(new Value(examesParaEnviar.get(i).getDataResultado() + "#" + examesParaEnviar.get(i).getCodigo()));
            code.setCodingSystem(new CodingSystem("TCC_RESULT_CODE"));
            result.setDescription(new Description(new Text(examesParaEnviar.get(i).getNome()), code));

            for (Resultado r : examesParaEnviar.get(i).getResultados()) {
                Test test = new Test();
                Code codeTest = new Code(new Value(code.getValue().getValue() +
                        "@" + r.getAmostra()+ "$" + r.getAtributo()), new CodingSystem("TCC_TEST_CODE"), null);

                if (r.getAtributo() == null || r.getAtributo().isEmpty()) {
                    test.setDescription(new Description(new Text(examesParaEnviar.get(i).getNome()),
                            codeTest));
                    TestResult testResult = new TestResult();
                    testResult.setValue(new Value("s/n"));

                    test.setTestResult(testResult);
                } else {

                    test.setDescription(new Description(new Text(r.getAtributo()),
                            codeTest));

                    TestResult testResult = new TestResult();
                    String resultado = r.getResultado();
                    if ((resultado != null || !resultado.isEmpty()) && resultado.contains(",")) {
                        resultado = resultado.replace(",", ".");
                    }
                    testResult.setValue(new Value(resultado));

                    test.setTestResult(testResult);
                }
                test.setCollectionDateTime(new DateTime(new Type(new Text(Type.COLLECTION_START_DATE)),
                        new ExactDateTime(formatData(examesParaEnviar.get(i).getDataResultado()))));

                result.getTests().add(test);
            }

            results.getResults().add(result);
        }

        XmlBlob ccrgElement = new XmlBlob();
        ccrgElement.setBlob(results.constructCcrString());
        newNotice.setXmlBlob(ccrgElement);

        try {
            Entry createdNotice = h9Service.insert(new URL(REGISTER_FEED_PATH), newNotice);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private String formatData(String xmlData) {
        String zuluData = new String();
        String data = xmlData.substring(0, 10);
        String hora = xmlData.substring(10, 15);

        String dataDia = data.substring(0, 2);
        String dataMes = data.substring(3, 5);
        String dataAno = data.substring(6, 10);

        zuluData = dataAno + "-" + dataMes + "-" + dataDia + "T" + hora + ":00" + "Z";

        return zuluData;
    }

    private String[] splitXmlData(String xmlData){
        String data = xmlData.substring(0, 10);
        String hora = xmlData.substring(10, 15);
        String dados[] = new String[2];
        dados[0] = data;
        dados[1] = hora;

        return dados;
    }

    private void processaExcecaoComunicacao(HttpServletRequest request, HttpServletResponse response) 
            throws ServletException, IOException {
        
        if (request.getParameter("token") != null) {
            HealthSample sp = new HealthSample();
            try {
                String token = sp.exchangeAuthSubToken(request.getParameter("token"),
                        request.getParameter("id"));

//                ArrayList<Exame> exames = smxmlp.extractExames();
//                PacienteXML pacienteXML = smxmlp.parseUser();

                String idString = pacienteEmCurso.getPaciente();
                int id = Integer.parseInt(idString);

                Paciente p = pacienteDAO.resgatarPaciente(id);

                p.setToken(token);

                pacienteDAO.criarOuAtualizarPaciente(p);

                HealthSample healthSample = new HealthSample();

                Object obj = healthSample.getProfileResults(p);
                if (obj instanceof List || obj == null) {
                    ArrayList<Exame> examesFiltrados = filtrarExames(examesEmCurso, (ArrayList<ArrayList>) obj);
                    if (examesFiltrados != null && examesFiltrados.size() > 0) {
                        //envia exames
                        enviarExames(examesFiltrados, pacienteEmCurso, p.getToken());
                        request.getRequestDispatcher("/upload.jsp").forward(request, response);
                    } else {
                        //mostra menssagem para o usuário avisando q o(s) exame(s) ja está lá
                        request.getSession().setAttribute("msg", SEM_MAIS_TESTES);
                        request.getRequestDispatcher("/error.jsp").forward(request, response);
                    }

                } else if (obj instanceof AuthenticationException) {
                    String authUrl = HealthSample.getAuthSubUrl(getCurrentUrl(request) +
                            "?action=handleConnException&id=" + Integer.parseInt(idString));
                    response.sendRedirect(authUrl);
                } else if (obj instanceof UnknownHostException) {
                    request.getSession().setAttribute("msg", ERRO_COMUNICAÇÃO);
                    request.getRequestDispatcher("/error.jsp").forward(request, response);
                }
                
            } catch (IOException ex) {
                Logger.getLogger(HealthSampleServlet.class.getName()).log(Level.SEVERE, null, ex);
            } catch (ServletException ex) {
                Logger.getLogger(HealthSampleServlet.class.getName()).log(Level.SEVERE, null, ex);
            } catch (HealthSampleException e) {
                System.err.println("Authentication exception: " + e.getMessage());
            }
        }else{
            request.getSession().setAttribute("msg", NO_AUTH);
            request.getRequestDispatcher("/error.jsp").forward(request, response);
        }
    }

    private void configuraProxy(HttpServletRequest request, HttpServletResponse response) {
        String server = request.getServerName();
        if(server.contains("tanenbaum")){
            System.setProperty("http.proxyHost", "10.65.16.2");
            System.setProperty("http.proxyPort", "3128");
        }
    }
}

